//
//  ClassViewController.m
//  ReviewDemo
//
//  Created by 龙 on 2018/3/17.
//  Copyright © 2018年 long. All rights reserved.
//

#import "ClassViewController.h"
#import <objc/runtime.h>
#import <objc/message.h>

#import "PeopleModel.h"

@interface ClassViewController ()

@end

@implementation ClassViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    objc_msgSend(self, @selector(inputIsaAddress));
    objc_msgSend(self, @selector(creatClass));
    objc_msgSend(self, @selector(lookClassInfo));

    self.view.backgroundColor = [UIColor whiteColor];
    
    self.view.backgroundColor = [UIColor whiteColor];
    objc_msgSend(self, @selector(lookClassInfo));
    UIImage *image = objc_msgSend(objc_getClass("UIImage"), @selector(imageNamed:),@"OC类结构图.jpg");
    
    UIImageView *imageView = objc_msgSend(objc_getClass("UIImageView"), sel_registerName("alloc"));
    imageView = objc_msgSend(imageView, sel_registerName("init"));
    objc_msgSend(imageView, @selector(setFrame:),self.view.bounds);
    objc_msgSend(imageView, @selector(setImage:),image);
    objc_msgSend(self.view, @selector(addSubview:),imageView);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


#pragma mark - 元类对象

- (void)lookClassInfo
{
    
    PeopleModel *people = objc_msgSend(objc_getClass("PeopleModel"), sel_registerName("alloc"));
    people = objc_msgSend(people, sel_registerName("init"));
    
    
    id isa = [people valueForKey:@"isa"];
    
    NSLog(@"isa ----- %@",isa);
    
    
    
    id superIsa = [people.superclass valueForKey:@"isa"];
    
    unsigned int methodCount = 0;
    Method *methodList = class_copyMethodList(people.class, &methodCount);
    for (int i = 0 ; i < methodCount; i ++)
    {
        Method meth = methodList[i];
        SEL sel = method_getName(meth);
        IMP imp = method_getImplementation(meth);
        NSLog(@"class_copyMethodList ----- %@ :  %s",NSStringFromSelector(sel),imp);
    }
    
    
    
    
    NSLog(@"class_getVersion ----- %d",class_getVersion(people.class));
    NSLog(@"class_getName ----- %s",class_getName(people.class));
    NSLog(@"class_getSuperclass ----- %@",class_getSuperclass(people.class));
    
    
    NSLog(@"superIsa ----- %@",superIsa);
    
    NSLog(@"%s__%ld",__func__,people.hash);
}


- (void)inputIsaAddress
{
    PeopleModel *people = objc_msgSend(objc_getClass("PeopleModel"), sel_registerName("alloc"));
    people = objc_msgSend(people, sel_registerName("init"));

    Class cla0 = [PeopleModel class];//返回PeopleModel类的本身的地址
    Class objCla0 = [people class]; //isa指向的PeopleModel类的地址
    Class ogcCla0 = object_getClass(people);// isa指向的PeopleModel类的地址  类对象
    
    NSLog(@"cla0 ----------- %p",cla0);   //0x106410e68
    NSLog(@"objCla0 -------- %p",objCla0);//0x106410e68
    NSLog(@"ogcCla0 -------- %p",ogcCla0);//0x106410e68

    
    NSLog(@"cla0 ----------- %@",cla0);   //PeopleModel
    NSLog(@"objCla0 -------- %@",objCla0);//PeopleModel
    NSLog(@"ogcCla0 -------- %@",ogcCla0);//PeopleModel
    
    /*
     分析一：当people为实例变量时, object_getClass(people)与[people class]输出结果一致，均返回该对象的isa指针，即指向PeopleModel类的指针。而[PeopleModel class]则直接返回PeopleModel类本身的地址，所以与前面两者返回的地址相同。
     */
    
    
    
    
    
    
    Class objCla1 = [cla0 class]; //返回PeopleModel元类对象的本身的地址
    Class ogcCla1 = object_getClass(cla0);//isa指向的PeopleModel元类的元类地址 元类对象
    
    NSLog(@"objCla1 -------- %p",objCla1);//0x106410e68
    NSLog(@"ogcCla1 -------- %p",ogcCla1);//0x106410e90

    NSLog(@"objCla1 -------- %@",objCla1);//PeopleModel
    NSLog(@"ogcCla1 -------- %@",ogcCla1);//PeopleModel
    /*
        分析二：
     此时cla0为PeopleModel的类对象，所以类方法[cla0 class]返回的cla0为self, 即PeopleModel类对象本身的地址，故结果与上面的地址相同。
     而ogcCla1返回的为PeopleModel元类的地址。
     */
    
    
    
    
    Class objCla2 = [ogcCla1 class]; //返回PeopleModel元类对象的本身的地址
    Class ogcCla2 = object_getClass(ogcCla1);//isa指向的PeopleModel元类的元类地址
    
    NSLog(@"objCla2 -------- %p",objCla2);//0x106410e90
    NSLog(@"ogcCla2 -------- %p",ogcCla2);//0x10b8abe58
    
    NSLog(@"objCla2 -------- %@",objCla2);//PeopleModel
    NSLog(@"ogcCla2 -------- %@",ogcCla2);//NSObject

    /*
     分析三：
     objCla2 为PeopleModel元类的元类的地址，那问题来了，某个类它的元类的元类的是什么类呢？这样下去岂不是元类无穷尽了？擒贼先擒王，我们先来看看根类NSObject的元类和它元类的元类分别是什么:
     */
    
    
    
    Class rootClass0 = object_getClass([NSObject class]);//返回NSObject元类(根元类)的地址
    Class rootClass1 = object_getClass(rootClass0);//返回NSObject元类(根元类)的元类地址

    NSLog(@"rootClass0 ----- %p",rootClass0);//0x10b8abe58
    NSLog(@"rootClass1 ----- %p",rootClass1);//0x10b8abe58

    NSLog(@"rootClass0 ----- %@",rootClass0);//NSObject
    NSLog(@"rootClass1 ----- %@",rootClass1);//NSObject
    
    /*
     分析四：根元类的isa指针指向自己，也就是根元类的元类即其本身。另外，可以发现ogcCla2的地址和根元类isa的地址相同，说明任意元类的isa指针都指向根元类，这样就构成一个封闭的循环。
     */
    
    NSLog(@"ogcCla0 is metaClass: %@",class_isMetaClass(ogcCla0) ? @"YES" : @"NO");
    NSLog(@"ogcCla1 is metaClass: %@",class_isMetaClass(ogcCla1) ? @"YES" : @"NO");
    //日志表明ogcCla0为类对象，而ogcCla1则为元类对象，这与我们上面的分析是一致的。
    
    
    
    /*
     总结一下实例对象，类对象以及元类对象之间的isa指向和继承关系的规则为:
     规则一: 实例对象的isa指向该类，类的isa指向元类(metaClass)
     规则二: 类的superClass指向其父类，如果该类为根类则值为nil
     规则三: 元类的isa指向根元类，如果该元类是根元类则指向自身
     规则四: 元类的superClass指向父元类，若根元类则指向该根类
     

     
     */
    
}


/*
 + (Class)class {
 return self; // 返回自身指针
 }
 
 - (Class)class {
 return object_getClass(self); // 调用'object_getClass'返回isa指针
 }
 
 
 Class object_getClass(id obj) {
 return _object_getClass(obj);
 }
 
 
 static inline Class _object_getClass(id obj) {
 #if SUPPORT_TAGGED_POINTERS
 if （OBJ_IS_TAGGED_PTR(obj)）{
 uint8_t slotNumber = ((uint8_t)(uint64_t) obj) & 0x0F;
 Class isa = _objc_tagged_isa_table[slotNumber];
 return isa;
 }
 #endif
 if (obj) return obj->isa;
 else return Nil;
 }
 
 显然_object_getClass函数就是返回对象的isa指针，也就是返回该对象所指向的所属类
 */



#pragma mark - 动态创建类


void SpendModeyFunction(id self , SEL _cmd)
{
    NSLog(@" ------- 儿子花钱了 -------- ");
}

void classMethodFunction(id self , SEL _cmd)
{
    NSLog(@"--------儿子的类方法-------");
}

- (void)creatClass
{
    Class newClass = objc_allocateClassPair(object_getClass(@"NSObjct"), "SonClass", 0);

    //添加实例方法
    if (class_addMethod(newClass, @selector(spendModey), (IMP)SpendModeyFunction, "v@:") == NO)
    {
        NSLog(@"Add method 'spendModey' failed!");
    }
    
    //添加类方法
    if (class_addMethod(object_getClass(newClass), @selector(classMethodTest), (IMP)classMethodFunction, "v@:") == NO)
    {
        NSLog(@"Add 类方法 'classMethodTest' failed!");
    }
    
    //添加变量
    if (class_addIvar(newClass, "_hobby", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *)) == NO)
    {
        NSLog(@"Add var '_hobby' failed!");
    }
    
    objc_registerClassPair(newClass);
    
    /*
       一、首先是调用objc_allocateClassPair为新建的类分配内存
       objc_allocateClassPair(Class superclass, const char * _Nonnull name, size_t extraBytes)
       (三个参数依次为newClass的父类，newClass的名称，第三个参数通常为0)
          从这个函数名字可以看到新建的类是一个pair, 也就是成对的类，那为什么新建一个类会出现一对类呢？
          是的，元类！类和元类是成对出现的，每个类都有自己所属的元类，所以新建一个类需要同时创建类以及它的元类。
     
       二、然后向newClass中添加变量及方法了
          注意：1、方法和变量必须在 objc_allocateClassPair 和 objc_registerClassPair 之间进行添加。
               2、若要添加类方法，需用objc_getClass(newClass)获取元类，然后向元类中添加类方法。因为实例方法是存储在类中的，而类方法则是存储在元类中。
     
       三、最后必须把newClass注册到运行时系统，否则系统是不能识别这个类的。
     */
    
    //创建一个实例变量
    id man = objc_msgSend(newClass, sel_registerName("alloc"));
    man = objc_msgSend(man, sel_registerName("init"));
    
    NSLog(@"新类的实例 ------- %p",man);


    
    unsigned int varCount = 0;
    Ivar *ivarList = class_copyIvarList(newClass, &varCount);
    NSLog(@"新类的变量数 ------- %d",varCount);
    for (int i = 0; i < varCount; i ++)
    {
        Ivar ivar = ivarList[i];
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        NSLog(@"变量 %@ 的 空值 为：%@",key,object_getIvar(man, ivar));
        object_setIvar(man, ivar, @"德玛西亚万岁");
        NSLog(@"变量 %@ 的 赋值 为：%@",key,object_getIvar(man, ivar));
    }
    free(ivarList);
    
}

@end
